#include <utility>

#include <utility>

#include "token.h"
#include "logger.h"


int startWithIgnoringCase(std::string a, std::string prefix, int offset) {
	int i = 0;
	unsigned int len = prefix.length();
	if (a.length() < offset + len)return 0;
	while (i < len) {
		char c = a[offset + i];
		c = tolower(c);
		if (c != prefix[i])return 0;
		i++;
	}
	return 1;
}
Token& TokenReader::nextToken() {
	int delta = 0;
	while (isspace(str[offset]) || str[offset] == '\n' || str[offset] == '\r') {
		if (str[offset] == '\n')lineCnt++;
		delta++;
		offset++;
	}
	if (str[offset] == '\0') return  cur = Token{ ENDFILE, "" };
	for (int i = Symbol::PLUS; i <= RBRACE; ++i) {
		if (str.compare(offset, templates[i].length(), templates[i]) == 0) {
			const Token& token = Token{ (Symbol)i, str.substr(offset, templates[i].length()) };
			lastOffset.push(delta + templates[i].length());
			offset += templates[i].length();
			return  cur = token;
		}
	}
	if (str[offset] == '\'') {
		offset++;
		delta++;
        bool err = false;
		if (str[offset] == '+' || str[offset] == '-' || str[offset] == '*' || str[offset] == '/' ||
			str[offset] == '_' || isalnum(str[offset])) {
			offset++; delta++;
			while(str[offset] != '\'' && str[offset] != '\0') err = true, offset++, delta++;
			if (str[offset] == '\'') {
				offset++; delta++;
				lastOffset.push(delta);
				if(err)log_error2('a', lineCnt);//非法符号或不符合词法 // more char
				return  cur = TokenChar{ str[offset - 2] };
			} // '' mismatch
			else log_error2('a', lineCnt);//非法符号或不符合词法
            lastOffset.push(delta);
            return  cur = TokenChar{ '?' };
		} // bad char
        while(str[offset] != '\'' && str[offset] != '\0') err = true, offset++, delta++;
        if (str[offset] == '\'') offset++, delta++;
        log_error2('a', lineCnt);//非法符号或不符合词法
        lastOffset.push(delta);
        return cur = TokenChar{ '?' };
    }
	else if (str[offset] == '\"') {
		offset++;
		delta++;
		int index = str.find_first_of('"', offset);
		if(index == offset){//""
            offset++;
            delta++;
            log_error2('a', lineCnt);//非法符号或不符合词法
            return cur = Token{Symbol::STRCON, "???"};// ERROR : BAD STRING
		}
		if (index != std::string::npos) {
			bool ok = true;
			for (int i = offset; i < index; ++i) {
				char c = str[i];
				if (c != 32 && c != 33 && c < 35 && c > 126) {
					ok = false;
					break;
				}
			}
			if (ok) {
				delta += index + 1 - offset;
                std::string ret = str.substr(offset,index - offset);
                for(auto it = ret.begin(); it != ret.end(); it++)if(*it == '\\'){
                   it = ret.insert(it, '\\');
                   it++;
                }
                const Token& token = Token{Symbol::STRCON, ret };
				offset = index + 1;
				lastOffset.push(delta);
				return  cur = token;
			}
			else {
                delta += index + 1 - offset;
                log_error2('a', lineCnt);//非法符号或不符合词法
                offset = index + 1;
                lastOffset.push(delta);
                return cur = Token{Symbol::STRCON, "???"};// ERROR : BAD STRING
            }
		}
		else {
            log_error2('a', lineCnt);//非法符号或不符合词法
            return cur = Token{Symbol::BAD, "Unidentified Token"};// ERROE "" mismatch
        }
	}



	for (int i = Symbol::CONSTTK; i <= RETURNTK; ++i) {
		unsigned int len = templates[i].length();
		if (startWithIgnoringCase(str, templates[i], offset) &&
			!(str.length() > offset + len && (isalnum(str[offset + len]) || str[offset + len] == '_'))) {
			const Token& token = Token{ (Symbol)i, str.substr(offset, len) };
			lastOffset.push(delta + len);
			offset += len;
			return  cur = token;
		}
	}

	if (isdigit(str[offset])) {
		int x = 0;
		int start = offset;
		while (true) {
			int d = str[offset] - '0';
			x += d;
			offset++;
			delta++;
            if(!isdigit(str[offset]))break;
            x *= 10;
		}

		if (str.length() < offset || (!isalpha(str[offset]) && str[offset] != '_')) {
			lastOffset.push(delta);
			const TokenInt& tokenInt = TokenInt{ str.substr(start, offset - start), x };
			return cur = tokenInt;
		} else {
            lastOffset.push(delta);
            log_error2('a', lineCnt);//非法符号或不符合词法
		    cur = Token{ Symbol::BAD, "Unidentified Token"}; // 123abc e.g.
            return cur;
        }
	}
	else if (isalpha(str[offset]) || str[offset] == '_') {
		int start = offset;
		while (isalnum(str[offset]) || str[offset] == '_') {
			offset++;
			delta++;
		}
		lastOffset.push(delta);
        std::string str_id = str.substr(start, offset - start);
		int len = str_id.length();
        for(int i  = 0; i < len; i++){
            str_id[i] = tolower(str_id[i]);
        }
        Token token = Token{Symbol::IDENFR, str_id};
        token.rawId = str.substr(start, offset - start);
        return cur = token;
	}

	error = true;
	lastOffset.push(delta);
	return  cur = Token{ Symbol::BAD, "Unidentified Token" }; //?????
}

void TokenReader::back() {
	int top = lastOffset.top();
	lastOffset.pop();
	while (top--) {
		offset--;
		if (str[offset] == '\n')lineCnt--;
	}
	if (lastOffset.empty()) {
		cur = {};
		return;
	}
	top = lastOffset.top();
	lastOffset.pop();
	while (top--) {
		offset--;
		if (str[offset] == '\n')lineCnt--;
	}
	nextToken();
}

Token& TokenReader::curToken() {
	return this->cur;
}

void TokenReader::logCurToken() {
    if(cur.symbol == IDENFR) {
        Log << SYMBOL_NAMES[cur.symbol] << ' ' << cur.rawId << std::endl;
    }
    else {
	    Log << SYMBOL_NAMES[cur.symbol] << ' ' << cur.string << std::endl;
	}
	if (cur.symbol == INTCON)Log << "<无符号整数>" << std::endl;
	else if (cur.symbol == STRCON)Log << "<字符串>" << std::endl;
}

int TokenReader::getLineCnt() const {
	return lineCnt;
}

int TokenReader::getPos() {
	int t = 0;
	int p = offset;
	if (str[p] == '\n') p--;
	while (p >= 0 && str[p] != '\n')p--, t++;
	return t;
}

int TokenReader::getPreLineCnt() const{
    int top = lastOffset.top(); int lpre = 0;
    for(int of = offset - 1; of >= offset - top; of --)if(str[of] == '\n')lpre++;
    return lineCnt - lpre;
}

int TokenReader::getPrePos() const{
    int t = 0;
    int p = offset - lastOffset.top();
    if (str[p] == '\n') p--;
    while (p >= 0 && str[p] != '\n')p--, t++;
    return t;
}

TokenChar::TokenChar(char c) : Token(Symbol::CHARCON, std::string({ c })) {
	this->data.c = c;
}

TokenInt::TokenInt(std::string x, int i) : Token(Symbol::INTCON, std::move(x)) {
	this->data.i = i;
}

bool isRelationSymbol(Symbol symbol) {
	return symbol >= LEQ && symbol <= NEQ;
}
